<?php
/*
 * Created on 20.12.2005
 *
 */

/**
 * Chain which runs the filters
 *
 * @property env $env Environment
 */
class phpinchains_FilterChain 
{

    /**
     * Points to a position in the chain
     * @var int
     */
    var $pointer = 0;
    /**
     * holds the chain
     * @var array
     */
    var $chain = array();
    /**
     * length of the chain
     * @var int
     */
    var $length;
    /**
     * number of filters that where called so far
     * @var int
     */
    var $numFilter = 0;
    /**
     * number of filters that where called so far
     * @var array
     */
    var $lastMatches = array();
    /**
     * number of filters that where called so far
     * @var int
     */
    var $lastFilter = -1;
    /**
     * Path where additional filters are located
     * @var string
     */
    var $filterpath = null;
    /**
     * Map where is stored, whether one filter of the groups has be executed
     * @var array
     */
    var $grouping = array();
    /**
     * Environment
     * @var env
     */
    var $env;

    /**
     * Constructor
     * @param env env Environment object which encapsulates the predefined vars
     * @param string filterpath Path from where the filters should be loaded
     *      if not specified, loaded from MODEL2_INCLUDE_PATH/phpinchains
     */
    function phpinchains_FilterChain (env $env, $filterpath = null)
    {
        $this->env        = $env;
        $this->filterpath = $filterpath;
    }

    /**
     * Get number of filters that have been invoked so far
     */
    function getRunFilter ()
    {
        return $this->numFilter;
    }

    /**
     * Makes a filter invisible for the run counter
     */
    function decRunFilter ()
    {
        $this->numFilter--;
    }

    /**
     * Add a filter to the chain
     *
     * A filter
     *
     * @param Filter filter Filter class to register - order is relevant
     */
    function addFilter ($filter)
    {
        $this->chain[] = $filter;
    }

    /**
     * Start the filter chain
     */
    function startChain ()
    {
        $this->length = count ($this->chain);
        $this->doFilter($this->env);
        if ($this->numFilter == 0)
        print "<b>ERROR:</b> No matching filter in chain.";
    }

    /**
     * Checks if a filter shoud be invoked
     *
     * @param string $match Regex to find out if filter shoud be invoked
     */
    function match ($match)
    {
        if ($match == '*')
        return true;
        $match = preg_replace("/\//",'\/', $match);
        $match = preg_replace("/\./",'\.', $match);
        $match = preg_replace("/\*(\?)?/",'(.*\\1)', $match);
        if ((defined("MODEL2_IGNORE_PATH_INFO") && MODEL2_IGNORE_PATH_INFO)
            || (!($path_info = $this->env->getServer("PATH_INFO")) && !($path_info = $this->env->getServer("ORIG_PATH_INFO")) && !($path_info = env::getInstance()->param('path_info'))))
        {
            // TODO check that ... concat module and action ... param names delivered by ParamFilters
            $run = & phpinchains_RuntimeConfig::getInstance();
            $action = $this->env->getAttribute('__actionParam__');
            $path_info = '/'.$run->get('moduleParam').($action?'/'.$action:'');
        }
        return preg_match('/^'.$match.'$/', $path_info, $this->lastMatches);
    }

    function getLastMatches() {
        return $this->lastMatches;
    }

    /**
     * Called by a filter to give control back to the filter chain
     *
     * @param env $env
     * @return void
     */
    function doFilter (env $env)
    {
        if ($this->pointer > 0) {
            $this->lastFilter = $this->pointer-1;
        }
        if ($this->lastFilter > -1 && isset($this->chain[$this->lastFilter][0]['capture']) && $this->chain[$this->lastFilter][0]['capture'] == 'true')
        {
            $ob = ob_get_contents();
            ob_end_clean();
            $env->setAttribute($this->chain[$this->lastFilter][0]['captureAttribute'], $ob);
        }
        // Skip not matching pointers
        while ($this->length > $this->pointer
            && ((isset($this->chain[$this->pointer][0]['group'])
                    && isset($this->grouping[$this->chain[$this->pointer][0]['group']]))
                || !$this->match($this->chain[$this->pointer][0]['match'])))
        {
            $this->pointer++;
        }
        if ($this->length <= $this->pointer) return;
        $filterclass = $this->chain[$this->pointer][0]['class'];

        if (isset($this->chain[$this->pointer][1]) && is_array($this->chain[$this->pointer][1])) {
            $matches = array();
            foreach ($this->chain[$this->pointer][1] as $n => $v) {
                if (!is_array($v) && preg_match_all('/\{(\d+)\}/', $v, $matches)) {
                    for ($i = 0; $i < count($matches[0]); $i++) {
                        $v = str_replace($matches[0][$i], $this->lastMatches[(int) $matches[1][$i]], $v);
                    }
                    $this->chain[$this->pointer][1][$n] = $v;
                }
            }
        }
        if ($this->filterpath != null && file_exists($this->filterpath.'/'.$filterclass.'.php')) {
            include_once($this->filterpath.'/'.$filterclass.'.php');
        } else {
            m2import('phpinchains/filter/'.$filterclass.'.php');
        }
        $obj = new $filterclass();
        if (isset($this->chain[$this->pointer][0]['group'])) {
            $this->grouping[$this->chain[$this->pointer][0]['group']] = true;
        }
        $obj->init($this->chain[$this->pointer][1]);
        if (isset($this->chain[$this->pointer][0]['capture']) && $this->chain[$this->pointer][0]['capture'] == 'true')
        {
            ob_start();
        }
        $this->pointer++;
        $this->numFilter++;
        $obj->doFilter($env, $this);
    }

}

?>